// CFrameGrabber.cpp : implementation file
//
// (c) Vadim Gorbatenko, 1999 
// gvv@mail.tomsknet.ru
// All rights reserved
//
// CFrameGrabber window class
// Release: 1199

#include "stdafx.h"
#include "FrameGrabber.h"

#define	DEFAULT_CAPTURE_DRIVER	0 //in most cases

#pragma comment(lib, "winmm.lib")
#pragma comment(lib, "vfw32.lib")

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

typedef LRESULT (CALLBACK *FRAMECALLBACK)(HWND , LPVIDEOHDR);
#define	IMAGEWIDTH(lpd) ((LPBITMAPINFOHEADER)lpd)->biWidth
#define	IMAGEHEIGHT(lpd) ((LPBITMAPINFOHEADER)lpd)->biHeight
#define	IMAGEBITS(lpd) ((LPBITMAPINFOHEADER)lpd)->biBitCount

LRESULT PASCAL _grabber_CallbackProc(HWND hWnd, LPVIDEOHDR lpVHdr);

CFrameGrabber	*theOnlyOneGrabber = NULL;
/////////////////////////////////////////////////////////////////////////////
// CFrameGrabber

CFrameGrabber::CFrameGrabber()
{
	dwLastCallback = NULL;
	imageData[0] = NULL;
	imageData[1] = NULL;
	nImagen = 0;
	vfs = 0;//offset image buffer
}

CFrameGrabber::~CFrameGrabber()
{
	if(imageData[0])
	{	HGLOBAL hg = GlobalHandle(imageData[0]);
		GlobalUnlock(hg);
		GlobalFree(hg);
	}
	if(imageData[1])
	{	HGLOBAL hg = GlobalHandle(imageData[1]);
		GlobalUnlock(hg);
		GlobalFree(hg);
	}
}


BEGIN_MESSAGE_MAP(CFrameGrabber, CWnd)
	//{{AFX_MSG_MAP(CFrameGrabber)
	ON_WM_DESTROY()
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()


/////////////////////////////////////////////////////////////////////////////
// CFrameGrabber message handlers
// create unvisible child window at (x,y)
BOOL CFrameGrabber::Create(int x, int y, CWnd *pParentWnd)
{
	if(theOnlyOneGrabber)
		return FALSE;

	ASSERT(!GetSafeHwnd());
	
	if(GetSafeHwnd()) {
		return FALSE;		//already created
		}
	
	HWND hWnd=capCreateCaptureWindow(_T("AviCap_FrameGrabber_lite"),
        							WS_CHILD|WS_CLIPSIBLINGS, x, y, 160, 120, 
									pParentWnd?pParentWnd->GetSafeHwnd():NULL, 0xffff);
	if(!hWnd)
		return FALSE;
	
	//initial setup for capture
/*	if(!capDriverConnect(hWnd, DEFAULT_CAPTURE_DRIVER)||
		!capSetCallbackOnFrame(hWnd, _grabber_CallbackProc)||
		!capPreview(hWnd, FALSE))
*/	
	if(capDriverConnect(hWnd, DEFAULT_CAPTURE_DRIVER))
	if(capSetCallbackOnFrame(hWnd, _grabber_CallbackProc))
	if(capPreview(hWnd, FALSE))
		;
	else
	{::DestroyWindow(hWnd);
		return FALSE;}
	//subclass window
	SubclassWindow(hWnd);
	
	update_buffer_size();
	if(!imageData[0] || !imageData[1])
	{::DestroyWindow(hWnd);
		return FALSE;}
	theOnlyOneGrabber = this;

	SetRGB();

	// save the palete for 8 bit modes
	char strTemp[256];
	CString strPal;
	GetTempPath(256, strTemp);
	strPal.Format("%s\\FrameGrabber.pal", strTemp);
	capPaletteSave(GetSafeHwnd(), (LPCSTR) strPal);

	return TRUE;
}

void CFrameGrabber::OnDestroy() 
{
	//disconnect from capture driver
	if(theOnlyOneGrabber) 
	{
		capSetCallbackOnFrame(GetSafeHwnd(), NULL);
		capDriverDisconnect(GetSafeHwnd());

		// deletes temporary file
		char strTemp[256];
		CString strPal;
		GetTempPath(256, strTemp);
		strPal.Format("%s\\FrameGrabber.pal", strTemp);
		CFile::Remove((LPCSTR) strPal);
	}

	CWnd::OnDestroy();
	theOnlyOneGrabber = NULL;
}

BOOL	CFrameGrabber::VideoFormatDialog()
{
	if(!GetSafeHwnd())	return FALSE;
	BOOL r = (BOOL)capDlgVideoFormat(GetSafeHwnd());
	update_buffer_size();
	return r;
}

BOOL	CFrameGrabber::VideoSourceDialog()
{
	if(!GetSafeHwnd())	return FALSE;
	BOOL r =(BOOL)capDlgVideoSource(GetSafeHwnd());
	update_buffer_size();
	return r;
}

LPBITMAPINFO	CFrameGrabber::GetDIB()
{
	ASSERT(nImagen == 0 || nImagen == 1);

	if(!imageData[nImagen])	
		return NULL;

	//get the new one of more then 20 ms passed
	if((timeGetTime()-dwLastCallback)>20)
		capGrabFrameNoStop(GetSafeHwnd());

	return (LPBITMAPINFO)imageData[nImagen];
}

LPBYTE			CFrameGrabber::GetImageBitsBuffer()
{
	if(!imageData[nImagen])	
		return NULL;

	//get the new one of more then 20 ms passed
	if((timeGetTime()-dwLastCallback)>20)
		capGrabFrameNoStop(GetSafeHwnd());

	return ((LPBYTE)imageData[nImagen])+vfs;
}

LPBYTE			CFrameGrabber::GetOldImageBitsBuffer()
{
	int nImagenOld = nImagen == 0 ? 1 : 0;
	if(!imageData[nImagenOld])	
		return NULL;

	//get the new one of more then 20 ms passed
	if((timeGetTime()-dwLastCallback)>20)
		capGrabFrameNoStop(GetSafeHwnd());

	return ((LPBYTE)imageData[nImagenOld])+vfs;
}

CSize		CFrameGrabber::GetImageSize()
{
	if(!imageData[nImagen])	
		return CSize(0,0);
	else	
		return CSize(IMAGEWIDTH(imageData[nImagen]), IMAGEHEIGHT(imageData[nImagen]));
}

DWORD		CFrameGrabber::GetImageBitsResolution()
{
	if(!imageData[nImagen])
		return 0;
	else	
		return IMAGEBITS(imageData[nImagen]);
}


// this is internal use only!
BOOL	validCallHint=FALSE;
void	CFrameGrabber::SetImageData(LPVOID data)
{
	ASSERT(validCallHint);
	// do not call this method indirectly!
	if(!validCallHint)	
		return;
	int nNewImage = nImagen == 0 ? 1 : 0;
	if(!imageData[nNewImage])		
		return;
	CopyMemory(((LPBYTE)imageData[nNewImage])+vfs,
				data, 
				IMAGEWIDTH(imageData[nNewImage])*IMAGEHEIGHT(imageData[nNewImage])*IMAGEBITS(imageData[nNewImage])/8);
	dwLastCallback	=  timeGetTime();
	nImagen = nNewImage;
	if(IsWindowVisible())
		InvalidateRect(NULL);

}

void	CFrameGrabber::update_buffer_size()
{
	vfs=capGetVideoFormatSize(GetSafeHwnd());
	if(!vfs)
	{
		if(imageData[0])
		{
			HGLOBAL hg = GlobalHandle(imageData[0]);
			GlobalUnlock(hg);
			GlobalFree(hg);
		}
		if(imageData[1])
		{
			HGLOBAL hg = GlobalHandle(imageData[1]);
			GlobalUnlock(hg);
			GlobalFree(hg);
		}
		vfs = 0;
		imageData[0] = NULL;
		imageData[1] = NULL;
		return;		
	}

	DWORD	lastBufferSize=0;
	if(imageData[0])
	{
		lastBufferSize = vfs +
		IMAGEWIDTH(imageData[0])*IMAGEHEIGHT(imageData[0])*IMAGEBITS(imageData[0])/8;
		ASSERT(lastBufferSize == (DWORD) vfs +
		IMAGEWIDTH(imageData[1])*IMAGEHEIGHT(imageData[1])*IMAGEBITS(imageData[1])/8);
	}
	
	DWORD newBufferSize;
	

	BITMAPINFO *lpBmpInfo =(BITMAPINFO	*)( new char[vfs]);
	
	((LPBITMAPINFOHEADER)lpBmpInfo)->biSize= sizeof BITMAPINFOHEADER;
	
	if(!capGetVideoFormat(GetSafeHwnd(), lpBmpInfo, (WORD)vfs))
	{
		delete lpBmpInfo;
		
		if(imageData[0])
		{	HGLOBAL hg = GlobalHandle(imageData[0]);
			GlobalUnlock(hg);
			GlobalFree(hg);
		}
		if(imageData[1])
		{	HGLOBAL hg = GlobalHandle(imageData[1]);
			GlobalUnlock(hg);
			GlobalFree(hg);
		}
		vfs = 0;
		imageData[0] = NULL;
		imageData[1] = NULL;
		return;		
	}

	newBufferSize = vfs + IMAGEWIDTH(lpBmpInfo)*IMAGEHEIGHT(lpBmpInfo)*IMAGEBITS(lpBmpInfo)/8;
	
	SetWindowPos(NULL,0,0,IMAGEWIDTH(lpBmpInfo),IMAGEHEIGHT(lpBmpInfo),
				SWP_NOMOVE|SWP_NOZORDER);

	if(newBufferSize > lastBufferSize)
	{
		if(imageData[0])//reallocate
		{	
			HGLOBAL hg = GlobalHandle(imageData[0]);
			GlobalUnlock(hg);
			GlobalFree(hg);
		}
		if(imageData[1])//reallocate
		{	
			HGLOBAL hg = GlobalHandle(imageData[1]);
			GlobalUnlock(hg);
			GlobalFree(hg);
		}

		imageData[0] = GlobalLock(GlobalAlloc(GMEM_MOVEABLE, newBufferSize+256/*add 256 bytes, just in case...*/));
		imageData[1] = GlobalLock(GlobalAlloc(GMEM_MOVEABLE, newBufferSize+256/*add 256 bytes, just in case...*/));
	}

	memcpy(imageData[0], lpBmpInfo, vfs);
	memcpy(imageData[1], lpBmpInfo, vfs);
	
	delete lpBmpInfo;
}

//////////////////////////////////////////////////////////////////
LRESULT PASCAL _grabber_CallbackProc(HWND hWnd, LPVIDEOHDR lpVHdr)
{
	ASSERT_VALID(theOnlyOneGrabber);
	validCallHint = TRUE;
	theOnlyOneGrabber ->SetImageData(lpVHdr->lpData);
	validCallHint = FALSE;
	return 0;
}

BOOL CFrameGrabber::SetImageSize(CSize &newSize)
{
	if(!GetSafeHwnd())
		return FALSE;

	// check if same size
	if (newSize == GetImageSize())
		return TRUE;

	vfs=capGetVideoFormatSize(GetSafeHwnd());
	if(!vfs)
		return FALSE;

	BITMAPINFO *pBmpInfo =(BITMAPINFO	*)( new char[vfs]);
	
	((LPBITMAPINFOHEADER)pBmpInfo)->biSize= sizeof BITMAPINFOHEADER;
	
	if(!capGetVideoFormat(GetSafeHwnd(), pBmpInfo, (WORD)vfs))
	{
		delete pBmpInfo;
		return FALSE;
	}
	pBmpInfo->bmiHeader.biWidth = newSize.cx;
	pBmpInfo->bmiHeader.biHeight = newSize.cy;
	pBmpInfo->bmiHeader.biSizeImage = newSize.cx * newSize.cy * (pBmpInfo->bmiHeader.biBitCount / 8);

	if(!capSetVideoFormat(GetSafeHwnd(), pBmpInfo, (WORD)vfs))
	{
		delete pBmpInfo;
		return FALSE;
	}
	else
		update_buffer_size();

	delete pBmpInfo;
	return TRUE;
}


BOOL CFrameGrabber::SetImageBitsResolution(WORD dwBits)
{
	if(!GetSafeHwnd())
		return FALSE;
	// check if same bits resolution
	if (dwBits == GetImageBitsResolution())
		return TRUE;

	vfs=capGetVideoFormatSize(GetSafeHwnd());
	if(!vfs)
		return FALSE;

	BITMAPINFO *pBmpInfo =(BITMAPINFO	*)( new char[vfs]);
	
	((LPBITMAPINFOHEADER)pBmpInfo)->biSize= sizeof BITMAPINFOHEADER;
	
	if(!capGetVideoFormat(GetSafeHwnd(), pBmpInfo, (WORD)vfs))
	{
		delete pBmpInfo;
		return FALSE;
	}

	pBmpInfo->bmiHeader.biBitCount = dwBits;
	pBmpInfo->bmiHeader.biSizeImage = pBmpInfo->bmiHeader.biHeight * pBmpInfo->bmiHeader.biWidth * (dwBits / 8);
	pBmpInfo->bmiHeader.biClrUsed = 0;
	
/*	char strTemp[256];
	CString strPal;
	if (dwBits <= 8)
	{
		GetTempPath(256, strTemp);
		strPal.Format("%s\\FrameGrabber.pal", strTemp);
		// get a valid color table
		capPaletteSave(GetSafeHwnd(), strPal);
	}
*/
	if(!capSetVideoFormat(GetSafeHwnd(), pBmpInfo, (WORD) pBmpInfo->bmiHeader.biSize))
	{
		delete pBmpInfo;
		return FALSE;
	}
	else
	{
		if (dwBits <=8)
		{
			char strTemp[256];
			CString strPal;
			GetTempPath(256, strTemp);
			strPal.Format("%s\\FrameGrabber.pal", strTemp);
			capPaletteOpen(GetSafeHwnd(), (LPCSTR) strPal);
		}
		update_buffer_size();
	}

	delete pBmpInfo;
	return TRUE;
}

BOOL CFrameGrabber::SetRGB()
{
	if(!GetSafeHwnd())
		return FALSE;

	vfs=capGetVideoFormatSize(GetSafeHwnd());
	if(!vfs)
		return FALSE;

	BITMAPINFO *pBmpInfo =(BITMAPINFO	*)( new char[vfs]);
	
	((LPBITMAPINFOHEADER)pBmpInfo)->biSize= sizeof BITMAPINFOHEADER;
	
	if(!capGetVideoFormat(GetSafeHwnd(), pBmpInfo, (WORD)vfs))
	{
		delete pBmpInfo;
		return FALSE;
	}
	pBmpInfo->bmiHeader.biPlanes = pBmpInfo->bmiHeader.biPlanes;

	if(!capSetVideoFormat(GetSafeHwnd(), pBmpInfo, (WORD)vfs))
	{
		delete pBmpInfo;
		return FALSE;
	}
	else
		update_buffer_size();

	delete pBmpInfo;
	return TRUE;
}
